package com.jsean.web;

import lombok.extern.slf4j.Slf4j;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.io.*;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.Properties;

@Slf4j
public class FileServer extends WebSocketServer {

    public FileServer(String ip, int port) {
        super(new InetSocketAddress(ip, port));
    }

    public void initialize(String base) {
        basePath = base;
    }

    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        log.info("Client connected: " + conn.getRemoteSocketAddress());
    }

    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        log.info("Client disconnected");
    }


    static Properties props = new Properties();
    private String basePath;
    private String REQUEST_WORD = "^^";
    private String SPLIT_WORD = "\\^\\^";
    private static final int CHUNK_SIZE = 1024 * 64; // 64KB chunks
    private static final String FILE_TRANSFER_FINISH = "FILE_TRANSFER_FINISH"; // 64KB chunks

    @Override
    public void onMessage(WebSocket conn, String message) {
        try {
            if (message.startsWith("DOWNLOAD_REQUEST" + REQUEST_WORD)) {
                // Handle file download request
                String[] parts = message.split(SPLIT_WORD);
                String requestedFile = parts[1];
                requestedFile = basePath + File.separatorChar + requestedFile;
                log.info("download file {}", requestedFile);
                File file = new File(requestedFile);

                if (file.exists()) {
                    long length = file.length();
                    log.info("file length = {}", length);
                    long sendLength = 0;
                    // Send file chunks
                    try (FileInputStream fis = new FileInputStream(file)) {
                        ByteBuffer buffer = ByteBuffer.allocate(CHUNK_SIZE);
                        int bytesRead;
                        
                        while ((bytesRead = fis.read(buffer.array())) != -1) {
                            sendLength += bytesRead;
                            buffer.limit(bytesRead);
                            conn.send(buffer);
                            buffer.clear();
                        }
                        log.info("Total send: {}", sendLength);
                        conn.send(FILE_TRANSFER_FINISH);
                    }
                } else {
                    conn.send("ERROR" + REQUEST_WORD + " File not found.");
                }
            } else {
                // Handle regular messages
                log.info("Received from client: " + message);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onError(WebSocket conn, Exception ex) {
        ex.printStackTrace();
    }

    @Override
    public void onStart() {
        log.info("Server started");
    }

    private static String getAppConfig(String key, String defaultValue) {
        try (InputStream is = FileServer.class.getClassLoader().getResourceAsStream("application.properties")) {
            props.load(is);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String ret = props.getProperty(key, "8090");
        if (ret == null) {
            return defaultValue;
        } else {
            return ret;
        }
    }

    public static void main(String[] args) {
        String ip = getAppConfig("app.ip", "0.0.0.0");
        int port = Integer.parseInt(getAppConfig("app.port", "8090"));
        String base = getAppConfig("app.server.base", "/root");
        FileServer server = new FileServer(ip, port);
        server.initialize(base);
        server.start();
        log.info("Server is running on ws://{}:{}", ip, port);
    }
}